{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "intro-inspect-runnables",
   "metadata": {},
   "source": [
    "# Inspect Runnables\n",
    "\n",
    "- Author: [ranian963](https://github.com/ranian963)\n",
    "- Peer Review: []()\n",
    "- Proofread : [Yun Eun](https://github.com/yuneun92)\n",
    "- This is a part of [LangChain Open Tutorial](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/13-LangChain-Expression-Language/02-InspectRunnables.ipynb)[![Open in GitHub](https://img.shields.io/badge/Open%20in%20GitHub-181717?style=flat-square&logo=github&logoColor=white)](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/13-LangChain-Expression-Language/02-InspectRunnables.ipynb)\n",
    "## Overview\n",
    "In this tutorial, we introduce how to **inspect** and visualize various components (including the graph structure) of a ```Runnable``` chain. Understanding the underlying graph structure can help diagnose and optimize complex chain flows.\n",
    "\n",
    "### Table of Contents\n",
    "- [Overview](#overview)\n",
    "- [Environment Setup](#environment-setup)\n",
    "- [Introduction to Inspecting Runnables](#introduction-to-inspecting-runnables)\n",
    "  - [Graph Inspection](#graph-inspection)\n",
    "  - [Graph Output](#graph-output)\n",
    "  - [Prompt Retrieval](#prompt-retrieval)\n",
    "\n",
    "  ### References\n",
    "- [LangChain: Runnables](https://python.langchain.com/api_reference/core/runnables.html)\n",
    "- [LangChain Open Tutorial](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n",
    "----\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "env-setup",
   "metadata": {},
   "source": [
    "## Environment Setup\n",
    "\n",
    "Set up the environment. You may refer to [Environment Setup](https://wikidocs.net/257836) for more details.\n",
    "\n",
    "**[Note]**\n",
    "- ```langchain-opentutorial``` is a package that provides a set of easy-to-use environment setup, useful functions and utilities for tutorials. \n",
    "- You can checkout the [```langchain-opentutorial```](https://github.com/LangChain-OpenTutorial/langchain-opentutorial-pypi) for more details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "install-libraries",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install langchain-opentutorial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "install-req",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_opentutorial import package\n",
    "\n",
    "package.install(\n",
    "    [\n",
    "        \"langsmith\",\n",
    "        \"langchain\",\n",
    "        \"langchain_core\",\n",
    "        \"langchain_community\",\n",
    "        \"langchain_openai\",\n",
    "        \"faiss-cpu\",\n",
    "        \"grandalf\",\n",
    "    ],\n",
    "    verbose=False,\n",
    "    upgrade=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "set-env",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Set environment variables\n",
    "from langchain_opentutorial import set_env\n",
    "\n",
    "set_env(\n",
    "    {\n",
    "        \"OPENAI_API_KEY\": \"\",\n",
    "        \"LANGCHAIN_API_KEY\": \"\",\n",
    "        \"LANGCHAIN_TRACING_V2\": \"true\",\n",
    "        \"LANGCHAIN_ENDPOINT\": \"https://api.smith.langchain.com\",\n",
    "        \"LANGCHAIN_PROJECT\": \"02-InspectRunnables\",\n",
    "    }\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dotenv-option",
   "metadata": {},
   "source": [
    "You can alternatively set ```OPENAI_API_KEY``` in ```.env``` file and load it. \n",
    "\n",
    "[Note] This is not necessary if you've already set ```OPENAI_API_KEY``` in previous steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "load-dotenv",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "introduction-to-inspecting-runnables",
   "metadata": {},
   "source": [
    "## Introduction to Inspecting Runnables\n",
    "\n",
    "LangChain ```Runnable``` objects can be composed into pipelines, commonly referred to as **chains** or **flows**. After setting up a ```runnable```, you might want to **inspect its structure** to see what's happening under the hood.\n",
    "\n",
    "By inspecting these, you can:\n",
    "- Understand the sequence of transformations and data flows.\n",
    "- Visualize the graph for debugging.\n",
    "- Retrieve or modify prompts or sub-chains as needed.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "graph-inspection",
   "metadata": {},
   "source": [
    "### Graph Inspection\n",
    "\n",
    "We'll create a runnable chain that includes a retriever from FAISS, a prompt template, and a ChatOpenAI model. Then we’ll **inspect the chain’s graph** to understand how data flows between these components.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "chain-setup",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n",
    "\n",
    "# Create a FAISS vector store from simple text data\n",
    "vectorstore = FAISS.from_texts(\n",
    "    [\"Teddy is an AI engineer who loves programming!\"], embedding=OpenAIEmbeddings()\n",
    ")\n",
    "\n",
    "# Create a retriever based on the vector store\n",
    "retriever = vectorstore.as_retriever()\n",
    "\n",
    "template = \"\"\"Answer the question based only on the following context:\\n{context}\\n\\nQuestion: {question}\"\"\"\n",
    "# Create a prompt template\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "# Initialize ChatOpenAI model\n",
    "model = ChatOpenAI(model=\"gpt-4o-mini\")\n",
    "\n",
    "# Construct the chain: (dictionary format) => prompt => model => output parser\n",
    "chain = (\n",
    "    {\n",
    "        \"context\": retriever,\n",
    "        \"question\": RunnablePassthrough(),\n",
    "    }  # Search context and question\n",
    "    | prompt\n",
    "    | model\n",
    "    | StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "graph-output",
   "metadata": {},
   "source": [
    "### Graph Output\n",
    "We can **inspect** the chain’s internal graph of nodes (steps) and edges (data flows)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "node-inspection",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get nodes from the chain's graph\n",
    "chain.get_graph().nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "edge-inspection",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get edges from the chain's graph\n",
    "chain.get_graph().edges"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "graph-print-ascii",
   "metadata": {},
   "source": [
    "We can also print the graph in an ASCII-based diagram to visualize the chain flow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "print-ascii",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain.get_graph().print_ascii()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "prompt-retrieval",
   "metadata": {},
   "source": [
    "### Prompt Retrieval\n",
    "Finally, we can retrieve the **actual prompts** used in this chain. This is helpful to see exactly what LLM instructions are being sent.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "get-prompts",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain.get_prompts()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
